/* *************************************************

vortex offline library v0.15

(c)2008 Yaao, Argenis Leon FORK from vortex library



	The Sync Strategy implemented here is take it from dojo

   http://docs.google.com/View?docid=dhkhksk4_8gdp9gr

 	*1 Sync downloads and caches our web user-interface AKA Slurp
	

	*2 Second, syncing happens automatically, without the user having to initiate it. 

	Dojo Sync automatically kicks off syncing when the web application first loads and

	we are on the network, and also if we are offline and we detect that network has 

	come back online.

	Especifically We upload any data that was changed or created while offline 

	
	*3 We download data to make available offline 



	vortex.offline - provides offline application access, syncing, etc

	vortex.storage - provides generic offline storage capabilities

	vortex.ajax - simple ajax library used by the vortex.offline module to check network availability

	vortex.utils - common helper functions

	vortex.utils.json - json conversion functions

	vortex.debug

	vortex.sql



CREDIT:

the vortex.offline and vortex.storage libraries wrap and rely on the Google Gears broswer

plug-in (http://gears.google.com/)



much of the functionality of the vortex.offline and vortex.storage libraries were inspired by

by Brad Neuberg's work on the dojo.offline library (http://dojotoolkit.org/offline). this is not 

intended as a replacement for that great library, but intended to be used when you dont need the 

entire dojo framework or are working with another javascript framework that does not already have 

offline support built in.



the vortex.utils.json library is taken from the following json proposal

(http://www50.brinkster.com/zlib13/demos/json/json-proposal-demo.htm)



************************************************* */



// create our own namespace so we can play nice with other libraries

if (!vortex) {
	var vortex = {};
}



/* *************************************************

vortex.offline



- first things first. you must call the init() method first, passing at least the application name.

you can also optionally pass in any configuration values (see below) as well as a list of resources

(urls) to cache (these will be *in addition* to any auto-discovered files unless you set the autoDiscovery

configuration value to false, in which case *only* these urls will be cached)



- configuration values:

	autoRefresh -			if true, all cached resources will be updated automatically when the application is online.

							if false, the resources are never updated after being initally cached.

							defaults to true.

	autoSync -				if true, the application will automatically try to sync when it comes back online.

							if false, you will have to manually call the sync method to synchronize changes.

							defaults to true.

	autoDiscovery -			if true, all supported resource types (css, images, etc) will automatically be cached for offline use.

							if false, you must specify which resources should be cached.

							defaults to true.

	networkPollInterval -	the amount of time (in seconds) to poll the network to see if the application is online or offline.

							defaults to 15 seconds.

	networkCheckUrl -		the url used to check network status. since the application uses an xhr request to determine connectivity,

							you should use a relative url or url that will not be restricted by the cross-domain policy.

							defaults to 'network_check.txt'

	networkTimeout -		the amount of time (in seconds) to wait for the networkCheckUrl before giving up and assuming the application is offline.

							defaults to 5 seconds.



 - the following events can be overridden to perform any custom actions that your application may require:

	onInitialized() - called when the offline features have fully been initialized (only if offline support is available)

	onNoOfflineSupport() - called once during initialization if offline support is not available (Google Gears not installed or not allowed to run)

	onNetworkStatusChange() - called whenever the network status changes from online to offline or vice-versa

	onFileCaptured(url, success, captureId) - called when a resource is captured

	onBeforeSync() - when the sync method is called, this event fires before any updates are performed

	onSyncComplete() - called once the sync process is completed

	onReplayAction(action) - during a sync operation, this is called for each action being replayed. this method should return true if the replay is successful, false otherwise



 - other methods

	discoverFiles() - scans the application and returns a list of referenced resources (images, css, etc).

					  if autoDiscover is true, this is the list of files that will be cached for the application.

					  if autoDiscover is false, you can use this method to get a list of files to add/remove from to create your manual list

					  note that the following ARE NOT automatically discovered:

						- dynamically generated scripts or content

						- resources referenced by <object> or <embed> tags

						- ActiveX controls or Java Applets

	isOffline() - returns true if the application is currently offline

	isOnline() - return true if the application is currently online

	hasOfflineSupport() - returns true if the offline components are available (Google Gears installed and allowed to run). return false otherwise.

	sync() - replays any recorded actions to keep the server in sync.

			 if autoSync is true, this method is automatically called when the application goes back online(It's calles too when the aplication init via onNetworkStatusChange).

			 if autoSync is false, you must manually call this method to replay any recorded actions.

	recordAction(action) - records an action taken by the user or application so that it can be played back later (used to keep the server in sync)



************************************************* */

vortex.offline = function(){
	var applicationName = "";
	var applicationUrl = "";
	var storeName = "";
	var error = "";
	var initialized = false;
	var offlineSupport = false;
	var offline = null;
	var localserver;
	var store;
	var filesToCapture;
	var isReplaying = false;
    var autoRefresh = true;
	var autoSync = true;
	var autoDiscovery = true;
	var networkPollInterval = 15;
	var networkCheckUrl = location.href + "network_check.txt";
	var networkTimeout = 5;
	var dbNamespace = "__vortex__offline__";

	// private functions
	var addUrl = function(url){
		;;;vortex.logger.log("addUrl:"+url);
		if(vortex.utils.sameDomain(url, window.location.href))
			filesToCapture[filesToCapture.length] = url;
	};

/*
 * 
 * 
 * culo
 */

	var captureCallback = function(url, success, captureId){
		 // If the file can not be downloaded
		 if (success==false){
		 	document.getElementById("sync-indicator").innerHTML = "<font color:#FF0000>SyncError:</font color>File "+url+" can not be downloaded";
		 }

		;;;vortex.logger.log("File "+ url+ " stored in Googgle Gears Cache. Success? " + success);
		// do any file-specific stuff here
		// also call the public event to let the application hook in here if desired
		vortex.offline.onFileCaptured(url, success, captureId);
	};

	var checkNetworkAvailability = function(){
		;;;vortex.logger.log("checkNetworkAvailability");
		// make xhr request to check for network connection
		vortex.ajax.setRequestTimeout(networkTimeout);
		vortex.ajax.getUrl(networkCheckUrl + "?x=" + Math.random(), function(url, xhr) {
		var previouslyOffline = offline;
		var status = 0;
			try{
				status = (xhr && xhr.status);
			}
			catch(err){};
			if(status == 200) {
				// We are online
				offline = false;
			}
			else {
				// We are offline
				offline = true;
			}
/* uncomment this if you want to test on your localhost, where it is hard to go 'offline'

//debug

var prefix = "http://localhost";

var isLocal = (applicationUrl.substr(0,prefix.length)==prefix);

if(!offline && isLocal && xhr.responseText != "1")

    offline = true;

*/

			if(previouslyOffline != offline || !initialized){
				// handle any status changes
				networkStatusChange(offline);
				
				// if we are just initializing, fire the public event

				if(!initialized){
					initialized = true;
					vortex.offline.onInitialized();
				}
				// also call the public event to let the application hook in here if desired

				vortex.offline.onNetworkStatusChange(offline); 

				// Update the widget
				if (offline) {
                    document.getElementById("network-indicator").innerHTML = "<div class='offline'>offline</div>";

                    document.body.className = "offline";
                    //log("Is Offline:" + offline);
                }
                else {
                    document.getElementById("network-indicator").innerHTML = "<div class='online'>online</div>";
                    document.body.className = "online";
                }
			}
	
			// set up to poll again
			window.setTimeout(checkNetworkAvailability, networkPollInterval * 1000);
		});
	};

	
	var networkStatusChange = function(offline){
		;;;vortex.logger.log("networkStatusChange");
		if(!offline && (autoRefresh || !initialized)){
			// we dont want to remove the store and re-create it (although that would be easier) 
			// because we might lose the connection again before all of the files are refreshed. 
			// instead, we do each file individually
			
			//Update Widget to show the file capture
			//alert(document.getElementById("network-indicator").innerHTML);
			document.getElementById("sync-indicator").innerHTML = "<font color:#FF0000>Downloading UI...</font color>";
			

			if(filesToCapture && filesToCapture.length){
				for(var f=0;f<filesToCapture.length;f++){
					var fileName = filesToCapture[f];
					store.remove(fileName);
					store.capture(fileName, captureCallback); // See *1 Caching Files
				}
				;;;vortex.logger.info("Start Google Gears store file capture!!!");
			}
		}
		// If 'autoSync' is true and we como from 'offline' we try to update the data form the server

		if(!offline && autoSync){//See *2
			vortex.offline.sync();
		}
	};

	// This method is executed by the sync() method
	var replayActions = function(){
		if(!isReplaying){
			isReplaying = true;
			var actions = vortex.storage.getAll(dbNamespace);
			// replay each action
			for(var key in actions){
				var action = actions[key];
				var success = vortex.offline.onReplayAction(action);
				if(success){
					// remove action from list
					vortex.storage.remove(key, dbNamespace);
				}
				else {
					// what if it fails?
				}
			}
			isReplaying = false;
		}
	};


	var parseStylesheets = function(sheet, files){
		try{
			// handle @import recursively (IE only)
			if(sheet.imports && sheet.imports.length){
				for(var i=0;i<sheet.imports.length;i++){
					files.push(sheet.imports[i].href);
					parseStylesheets(sheet.imports[i], files);
				}
			}
			
			var rules = (sheet.cssRules || sheet.rules);
			if(rules && rules.length){
				for(var r=0;r<rules.length;r++){
					var rule = rules[r];

					// handle @imports recursively (FF)
					if(rule.styleSheet) parseStylesheets(rule.styleSheet, files);

					var text = (rule.cssText || rule.style.cssText);
					if(text){
						var matches = text.match(/url\(\s*([^\) ]*)\s*\)/i);
						if(matches){
							for(var j = 1; j < matches.length; j++){
								files.push(matches[j]);
							}
						}
					}
				}
			}
		}catch(err){
		};
	};

	// public interface

    return {
		// public events that can be overridden per application
		onInitialized : function() {},
		onNoOfflineSupport : function() {},
		onNetworkStatusChange : function(isNowOffline) {},
		onFileCaptured : function(url, success, captureId) {},
		onBeforeSync : function() {},
		onSync: function (){},
		onSyncComplete : function() {},
		onReplayAction : function(action) {return true;},

		// public methods

		init : function(appName, config, files) {
			/*;;;var headID = document.getElementsByTagName("head")[0];         
			;;;var newScript = document.createElement('script');
			;;;newScript.type = 'text/javascript';
			;;;newScript.src = 'miel.js';
			;;;headID.appendChild(newScript);	*/
			;;;vortex.logger.init();
			;;;vortex.logger.log("vortex.offline.init");
			
			
			// get basic application information
			applicationName = appName;
			applicationUrl = window.location.href;
			storeName = vortex.utils.replaceNonAlphaNumericCharacters(applicationUrl);
			dbNamespace += storeName;

			// handle overriding any configuration details
			if(config){
				if(config.autoRefresh) autoRefresh = config.autoRefresh;
				if(config.autoSync) autoSync = config.autoSync;
				if(config.autoDiscovery) autoDiscovery = config.autoDiscovery;
				if(config.networkPollInterval) networkPollInterval = config.networkPollInterval;
				if(config.networkCheckUrl) networkCheckUrl = config.networkCheckUrl;
				if(config.networkTimeout) networkTimeout = config.networkTimeout;
			}

			// initialize offline support

			;;;vortex.logger.log("Google Gears Support?");
			if(window.google && google.gears) {
				try{
					// if the user denies google gears the rights to run, this will fail
					localserver = google.gears.factory.create('beta.localserver', '1.0');
					
					store = localserver.createStore(storeName);
					
					offlineSupport = true;
					;;;vortex.logger.log(offlineSupport);
				}
				catch(err){
					txt="There was an error on this page.\n\n";
 					txt+="Error description: " + err.description + "\n\n";
  					;;;vortex.logger.error(txt);
				};
			}
			
			if(!offlineSupport){
				;;;vortex.logger.warn(offlineSupport);
				vortex.offline.onNoOfflineSupport();
				vortex.logger.info("Download Google Gears: <a style='padding-left:3px;border-bottom:solid #D7D7D7 1px; background-color:#FFFFFF; color:#000000' href='http://gears.google.com/'>Google Gears</a>");
				// Update Widget to Unsupported
				document.getElementById("network-indicator").innerHTML = "<div class='unsupported'>no offline support</div>";
                		document.body.className = "unsupported";
				return;
			}
			
			;;;vortex.logger.log("Start Slurp!!!");
			
			// determine files needed to run offline
			filesToCapture = [];
			
			if(autoDiscovery){
				;;;vortex.logger.log("autoDiscovery:" + autoDiscovery);
			
				// find files and add them to the list
				var autoFiles = this.discoverFiles();
				;;;vortex.logger.log("Files discovered in the Slurp: " + autoFiles);
			
				// add to list
				if(autoFiles && autoFiles.length && autoFiles.length > 0) {
					for(var f=0;f<autoFiles.length;f++){
						addUrl(autoFiles[f]);
					}
				}
			}
			
			;;;vortex.logger.log("Start Slurp for specific files");
			
			// add any additional files specified
			
			if(files && files.length && files.length > 0 && null!=files) {
				for(var f=0;f<files.length;f++){
					addUrl(files[f]);
				}
			}

			// do an initial network check and then start polling at regular intervals
			// so we know when we go on/offline
			;;;vortex.logger.log("Verifyng network Availabitlity");
			checkNetworkAvailability();
			return true;

		},

		discoverFiles : function() {
			;;;vortex.logger.log("discoverFiles");
			// return a list of resources required by this page, including:
			//    - the page calling this script
			//    - any referenced javascript files
			//    - any inline images
			// note that the following ARE NOT automatically discovered:
			//    - any referenced stylesheets (including @import)
			//    - any images referenced in the linked stylesheets
			//    - dynamically generated scripts or content
			//    - resources referenced by <object> or <embed> tags
			//    - ActiveX controls or Java Applets
			var elements;
			var e;
			var i;
			var files = [];
			
			// this page
			files.push(window.location.href);				
			
			// referenced scripts
			// Important: If you use cakePHP it automaticaly add the javascript
			// to the file. So its not necesary to call the js external file */
			
			elements = document.getElementsByTagName("script");
			;;;vortex.logger.log("Getting script tags...");
			
			
			for(i=0; i<elements.length-1; i++){
				e = elements[i];
				//vortex.logger.log("element No "+ i+":" +e);
				// Internet Explorer Mobile may thrown an exception if
				// the attribute in getAttribute do not exist.
				// 
				try{
					vortex.logger.log("Attribute: " + e.getAttribute('src'));
					eSrc = e.getAttribute('src');
					// If we can get the attribute verify if is not null or blank
					
					if (eSrc!= null && eSrc!= "") {
						;;;vortex.logger.log("Attribute: "+eSrc);
						files.push(eSrc);
						;;;vortex.logger.log("script: " + e.getAttribute('src'));
					}
				}
				catch(err){
					;;;vortex.logger.log("NOT exist attribute *src* in tag *script* ");
				};
			}
			
			// referenced inline images
			;;;vortex.logger.log("Getting img tags...");
			elements = document.getElementsByTagName("img");
			
			for(i=0;i<elements.length-1;i++){
				e = elements[i];
				//vortex.logger.log("element No "+ i+":" +e);
				// Internet Explorer Mobile may thrown an exception if
				// the attribute in getAttribute do not exist.
				// 
				try{
					vortex.logger.log("Attribute:"+e.getAttribute('src'));
					eScr = e.getAttribute('src');
					
					// If we can get the attribute verify if is not null or blank
					if (eSrc!= null && eSrc!= "") {
						;;;vortex.logger.log("Attribute:"+eSrc);
						files.push(eSrc);
						;;;vortex.logger.log("script:"+e.getAttribute('src'));
					}
				}
				catch(err){
					;;;vortex.logger.log("NOT exist attribute *src* in tag *img*");
				};
			}
			
			// referenced stylesheets
			;;;vortex.logger.log("Getting link tags AKA CSS...");
			elements = document.getElementsByTagName("link");
			for(i=0;i<elements.length;i++){
				e = elements[i];
				try{
					eRel = e.getAttribute("rel");
					if (eRel && eRel.toLowerCase() == "stylesheet") {
						eHref = e.getAttribute("href");
						if (eHref!=null && eHref!='')
						{
							;;;vortex.logger.log("link:"+eHref);
							files.push(eHref);
						}
					}
				}
				catch(err){
					;;;vortex.logger.log("NOT exist attribute *href* in tag *link*");
				};
			}
			
			// @import and url()
			// AL PARECER @import y url() NO DEBERIAN SER UTILIZADAS PARA DESARROLLO EN MOVILES
			/*if(document.styleSheets && document.styleSheets.length){
				for(var i=0;i<document.styleSheets.length-1;i++){
					parseStylesheets(document.styleSheets[i], files);
				}
			}*/
			;;;vortex.logger.log("Slurp Finished!!!");
			return files;
		},
		
		isOnline : function() {
			return !offline;
		},
		
		isOffline : function() {
			return offline;
		},
		
		hasOfflineSupport : function() {
			return offlineSupport;
		},

		sync : function() {
			// fire public event
			vortex.offline.onBeforeSync();
			
			// replay any recorded actions
			;;;vortex.logger.info("replayActions");
			document.getElementById("sync-indicator").innerHTML = "Uploading new data...";
			replayActions(); // See *2 Upload local actions
			
			;;;vortex.logger.info("onSync");
			document.getElementById("sync-indicator").innerHTML = "Downloading new data...";
			vortex.offline.onSync(); //See *3 Download new data from the server

			// fire the public event
			;;;vortex.logger.info("Sync Successfull");
			document.getElementById("sync-indicator").innerHTML = "Sync Successfull";
			vortex.offline.onSyncComplete();
		},
		
		recordAction : function(action){
			;;;vortex.logger.log("recordAction");
			var key = new Date().toString();
			vortex.storage.put(key, action, dbNamespace);
		}
    }
}();

/* *************************************************
vortex.storage


- the vortex.storage object is a singleton that is automatically initialized upon access, so 
you do not have to initialize it and can access it at any time by using vortex.storage.method()

- all items are stored in a common database and table. if you want to segregate your data, use
the 'namespace' parameter to keep different types of data or different parts of your application
stored individually

- if Google Gears is not installed or not allowed to run, the storage methods will not do anything.
you can use vortex.storage.isAvailable() to check if storage capabilites are available.

- all data is stored as a JSON representation of the object. if you pass in objects with circular
references, it will blow up.

- methods
	get(key, namespace) - retrives the object with the given key and namespace
	getAll(namespace) - retrieves a recordset containing all records in the given namespace
	put(key, val, namespace) - save the val object using the specified key and namespace.
							   if an item already exists with the given key and namespace, it will be verwritten.
	remove(key, namespace) - removes the item with the given key and namespace
	clear(namespace) - removes all items currently saved in the given namespace
	isAvailable() - returns true if the storage provider is available (Google Gears is installed and allowed to run), otherwise returns false



************************************************* */

vortex.storage = function(){
	var isAvailable = false;
	var dbName = "__vortex";
	var tableName = "__vortex__storage";
	var db = null;
	
	// initialize offline support
	if(window.google && google.gears) {
		try{
			db = google.gears.factory.create('beta.database', '1.0');
			db.open(dbName);

			db.execute("CREATE TABLE IF NOT EXISTS " + tableName + "( "
						+ " namespace TEXT, "
						+ " key TEXT, "
						+ " value TEXT "
						+ ")"
					);
			db.execute("CREATE UNIQUE INDEX IF NOT EXISTS namespace_key_index" 
						+ " ON " + tableName
						+ " (namespace, key)"
					);
			
			isAvailable = true;
		}
		catch(err){};
	}

	return {
		// Get an specific action from the database
		get : function(key, namespace){
			if(isAvailable){
				var val = null;

				var rs = db.execute("SELECT * FROM " + tableName
							+ " WHERE namespace = ? AND "
							+ " key = ?",
							[namespace, key]);
				if(rs.isValidRow()) {
					val = rs.field(2);
					return vortex.utils.json.parse(val);
				}
				else {
					return null;
				}
			}

			else

				return null;

		},

		// Get all the actions from the database
		getAll : function(namespace){
			if(isAvailable){
				var list = [];
				var rs = db.execute("SELECT * FROM " + tableName
							+ " WHERE namespace = ?",
							[namespace]);
				while (rs.isValidRow()) {
					list[rs.field(1)] = vortex.utils.json.parse(rs.field(2));
					rs.next();
				}
				rs.close();
				return list;
			}
			else
				return null;
		},

		// Put an action in the database		
		put : function(key, val, namespace){
			if(isAvailable){
				var value = vortex.utils.json.stringify(val);
				
				vortex.storage.remove(key, namespace);
				db.execute("INSERT INTO " + tableName
						+ " VALUES (?, ?, ?)",
						[namespace, key, value]);
				return true;
			}
			else
				return false;
		},
		
		// Remove and action from the database
		remove : function(key, namespace){
		if(isAvailable){
				db.execute("DELETE FROM " + tableName
							+ " WHERE namespace = ? AND key = ?",
							[namespace, key]);
				return true;
			}
			else
				return false;
		},
		
		clear : function(namespace){
			if(isAvailable){
				db.execute("DELETE FROM " + tableName
							+ " WHERE namespace = ?",
							[namespace]);
				return true;
			}
		else
				return false;
		},
		
		isAvailable : function(){
			return isAvailable;
		}
	}
}();
/**

 * 

 */

vortex.sql = function(){

// Private members

return{
	// Public members
	dbName:null,
	exec: function(sql,args){
		try {
			// get the Gears Database object
			this._initDb();
		
			// see if we need to open the db; if programmer

			// manually called vortex.sql.open() let them handle

			// it; otherwise we open and close automatically on

			// each SQL execution
			if (!this._dbOpen) {
				this.open();
				this._autoClose = true;
			}
			// determine our parameters
			/*var sql = null;
			alert  ("params:"+ params);
			// convert the params to an array
			var args = vortex.utils.toArray(params);
			alert("args:"+args);
			sql = args.splice(0, 1)[0];
			alert (sql);*/
			// 'args' now just has the SQL parameters

			// print out debug SQL output if the developer wants that

			/*if (debug) {

				_printDebugSQL(sql, args);

			}*/

			

			// execute the SQL and get the results

			//alert("db.execute:"+sql+"args:"+args);

			

			//var rs = this.db.execute(sql, args);

			var rs = this.db.execute(sql,args);

			// Gears ResultSet object's are ugly -- normalize
			// these into something JavaScript programmers know
			// how to work with, basically an array of 
			// JavaScript objects where each property name is
			// simply the field name for a column of data
			var results=[];

			while (rs.isValidRow()) {
				var row = {};
				
				for (var i = 0; i < rs.fieldCount(); i++) {
					var fieldName = rs.fieldName(i);
					var fieldValue = rs.field(i);
					row[fieldName] = fieldValue;
					
				}
					results.push(row);
					rs.next();
			}

			rs.close();

			if (this._autoClose) {
				this.close();
			}
			
			return results;
		} 
		catch (exp) {
			exp = exp.message || exp;
			alert(exp);
			//console.debug("SQL Exception: " + exp);
			if (this._autoClose) {
				try {
					close();
				} 
				catch (e) {
				/*console.debug("Error closing database: " 
				 + e.message||e);*/
				}
			}
			
			throw exp;
		}
		
	},
	
		open: function(dbName){
		if(this._dbOpen && (!dbName || dbName == this.dbName)){
			return;
		}

		if(!this.dbName){
			this.dbName = "dot_store_" 
				+ window.location.href.replace(/[^0-9A-Za-z_]/g, "_");
				;;;vortex.logger.log("vortex.sql.open.dbName:" + this.dbName);
		}
		
		if(!dbName){
			dbName = this.dbName;
		}
		
		try{
			this._initDb();
			this.db.open(dbName);
			this._dbOpen = true;
			
		}catch(exp){
			throw exp.message||exp;
		}
	},

		close: function(dbName){
			
		// on Internet Explorer, Google Gears throws an exception
		// "Object not a collection", when we try to close the
		// database -- just don't close it on this platform
		// since we are running into a Gears bug; the Gears team
		// said it's ok to not close a database connection
		
		// Test for MSIE. Take it from http://www.javascriptkit.com/javatutors/navigator.shtml
		if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)){ return; }
		
		if(!this._dbOpen && (!dbName || dbName == this.dbName)){
			return;
		}
		
		if(!dbName){
			dbName = this.dbName;
		}
		
		try{
			this.db.close(dbName);
			this._dbOpen = false;
		}catch(exp){
			throw exp.message||exp;
		}
	},

	_initDb: function(){
		if(!this.db){
			try{
				this.db = google.gears.factory.create('beta.database');
			}catch(exp){
				alert(exp);
				//dojo.setObject("google.gears.denied", true);
				//dojox.off.onFrameworkEvent("coreOperationFailed");
				throw "Google Gears must be allowed to run";
			}
		}
	}
	}
}();

/* *************************************************
vortex.ajax



- this is a simple xhr wrapper that is mainly used by the vortex.offline module to check network connetivity.

as such, it is not intended to be a replacement for a your regular full-featured ajax library as much as it 

is a 'quick and easy' way to perform simple xhr calls without any unnecessary functionality



************************************************* */

vortex.ajax = function(){

	var timeout = 5;
	var doCallback = function(xhr, callback, callbackArguments){
		// we replace the second argument (callback function) with the xhr object)
		// kind of cheesy, but it is nice and easy and works well since this is not
		// a full-feldged ajax library
		callbackArguments[1] = xhr;
		callback.apply(this, callbackArguments);
		xhr = null;
	};

	return {
		setRequestTimeout : function(t){
			timeout = t;
		},


		getUrl : function(url, callback){
			var xhr = null;
			var callbackArguments = arguments;

			// branch for native XMLHttpRequest object
			if(window.XMLHttpRequest) {
    			try {
					xhr = new XMLHttpRequest();
				} catch(e) {
					xhr = null;
				}
			// branch for IE/Windows ActiveX version
			} else if(window.ActiveXObject) {
       			try {
        			xhr = new ActiveXObject("Msxml2.XMLHTTP");
      			} catch(e) {
        			try {
          				xhr = new ActiveXObject("Microsoft.XMLHTTP");
        			} catch(e) {
          				xhr = null;
        			}
				}
			}
			
			if(xhr) {
				xhr.onreadystatechange = function () {
				if(xhr && xhr.readyState == 4) {
					// cancel the timeout timer
					window.clearTimeout(timer);
									
					// handle the callback
					doCallback(xhr, callback, callbackArguments);
					}
				}
			}
	
			if(xhr) {
				var method = "GET";
				var contentType = "text/plain";
				var data = "";
				
				xhr.open(method, url, true);
				xhr.setRequestHeader("Content-Type", contentType);
				xhr.send(data);
				
				timer = window.setTimeout(function() {
					xhr.onreadystatechange = function(){};
					try{xhr.abort();}catch(err){}
					// even though we timed out, lets handle this case like a successful error
					doCallback(xhr, callback, callbackArguments);
				}, timeout * 1000);
			}
		}
	}
}();
/**
 * vortex.logger
 * Functions to create a console and send log information
 */
vortex.logger=function(){
	var _console=null;
	var _consoleName = "vortex-console";
	var _consoleBodyName = "body-console";
	var _consoleTitleName = "title-console";
	var _debug = true;

	// CSS styles padding and overflow are not supported by Internet Explorer Mobile 6.1 they are used for browser who support

	return{
		init:function(obj){
			//txt = "<div class='console' id='" + _consoleName + "'></div>" + document.body.innerHTML;

			/*
mydiv = document.createElement('div');	

			myid = document.createAttribute('id');

			mydiv.setAttribute(myid,'vortexconsole'); 

    		mydiv.setAttributeNode(myid); 

		document.body.insertBefore(mydiv,document.body.firstChild);*/

			document.getElementById(_consoleName).innerHTML+=("<div id='"+ _consoleTitleName+"'><a href='#' onclick='vortex.logger.clearConsole();'>Clear</a><a href='#' onclick='vortex.logger.closeConsole();'>    Close</a></div>");

			document.getElementById(_consoleName).innerHTML+=("<div id='"+ _consoleBodyName +"'></div>");

		},
		closeConsole: function(){
			document.getElementById(_consoleBodyName).innerHTML= "";
			document.getElementById(_consoleName).style.visibility= "hidden";
			document.getElementById(_consoleName).style.height= "0px";
		},
	
		clearConsole: function(){
			document.getElementById(_consoleBodyName).innerHTML= "";
			document.getElementById(_consoleBodyName).style.height= "80px";
		},
		/**

		 * 

		 * @param {Object} obj

		 */

		log: function(obj){
			if(!_debug) {
				return;
			}
			// Open Popup where the Log information will be written
			document.getElementById(_consoleBodyName).innerHTML+=("<div style='padding-left:3px;border-bottom:solid #D7D7D7 1px; color:#000000';background-color:#FFFFFF;>"+obj+"</div>");
		},
		debug: function(obj){
			if(!_debug) {
				return;
			}
			// Open Popup where the Log information will be written
			document.getElementById(_consoleBodyName).innerHTML+=("<div style='padding-left:3px;border-bottom:solid #D7D7D7 1px; color:#000000'; background-color:#FFFFFF;>"+obj+"</div>");

		},
		/**
		 * It can be use to print information usefull for the final user
		 * @param {Object} obj
		 */
		info:function(obj){
			// Info is always printed			
			document.getElementById(_consoleBodyName).innerHTML+=("<div style='padding-left:3px;border-bottom:solid #D7D7D7 1px; background-color:#FFFFFF; color:#000000'>Info:"+obj+"</div>");

		},
		/**
		 * 
		 * @param {Object} obj
		 */
		warn:function(obj){
			if(!_debug){
				return;
			}
			
			document.getElementById(_consoleBodyName).innerHTML+=("<div style='padding-left:3px;border-bottom:solid #D7D7D7 1px; background-color:#00FFFF; color:#000000'>Warning:"+obj+"</div>");
			},
		/**
		 * It can be use to print on error function
		 * @param {Object} obj

		 */
		error:function(obj){
			if(!_debug){
				return;
			}

			document.getElementById(_consoleBodyName).innerHTML+=("<div style='padding-left:3px;border-bottom:solid #D7D7D7 1px; background-color:#FFFFE0; color:#FF0000'>Error:"+obj+"</div>");
		},

		/**

		 * Print a object's Attributes

		 * @param {Object} el Object wich you want to know its Attributes

		 */

		inspector:function (el) {
			var str ="";
			for (var i in el){
				str+=i + ": " + el.getAttribute(i) + "\n";
			}
			log(str);
		}
	}
}();

/* *************************************************

vortex.utils & vortex.utils.json



- just a hodge-podge of utility functions used by the other vortex libraries



- the json module includes some regexs that look like unterminated strings, so some minification tools might choke on it



- if you would prefer to use another json library, you can override the following methods:

	vortex.utils.json.stringify(obj) - where obj is the object to convert. returns a json-formatted string

	vortex.utils.json.parse(json) - where json is the json-formatted string to parse. returns a javascript object



************************************************* */

vortex.utils = function(){
	return {
		startsWith : function(input, prefix){
			return (input.substr(0,prefix.length)==prefix);
		},

		replaceNonAlphaNumericCharacters : function(input){
			var output = input.replace(/[^0-9A-Za-z_]/g, "_");
			return output;
		},
		
		sameDomain : function(url1, url2){
			url1 = url1.toLowerCase();
			url2 = url2.toLowerCase();

			// exact same url == match
			if(url1 == url2) return true;
			// relative url == match
			if(!vortex.utils.startsWith(url1, "http://") && !vortex.utils.startsWith(url1, "https://")) return true;
			// same domain == match
			var index1 = url1.indexOf("/", 8);
			var index2 = url2.indexOf("/", 8);
			if(index1 == -1) index1 = url1.length - 1;
			if(index2 == -1) index2 = url2.length - 1;
			var domain1 = url1.substring(0, index1);
			var domain2 = url2.substring(0, index2);
			if(domain1 == domain2) return true;
			// all else == fail
			return false;
		},
		
		json : function() {
			// Test for modern browser (any except IE5).
			var JS13 = ('1'.replace(/1/, function() { return ''; }) == '');

			// CHARS array stores special strings for encodeString() function.
			var CHARS = {
				'\b': '\\b',
				'\t': '\\t',
				'\n': '\\n',
				'\f': '\\f',
				'\r': '\\r',
				'\\': '\\\\',
				'"' : '\\"'
			};

			for (var i = 0; i < 32; i++) {
				var c = String.fromCharCode(i);
				if (!CHARS[c]) CHARS[c] = ((i < 16) ? '\\u000' : '\\u00') + i.toString(16);
			};
			  
			function encodeString(str) {
				if (!/[\x00-\x1f\\"]/.test(str)) {
				  return str;
				} else if (JS13) {
				  return str.replace(/([\x00-\x1f\\"])/g, function($0, $1) {
					return CHARS[$1];
				});
				} else {
				var out = new Array(str.length);
				for (var i = 0; i < str.length; i++) {
					var c = str.charAt(i);
					out[i] = CHARS[c] || c;
				}
				return out.join('');
				}
			};

			return {
				stringify : function (arg) {
					switch (typeof arg) {
						case 'string'   : return '"' + encodeString(arg) + '"'; // break command is redundant ere and below.
						case 'number'   : return String(arg);
						case 'object'   : 
							if (arg) {
								var out = [];
								if (arg instanceof Array) {
								for (var i = 0; i < arg.length; i++) {
									var json = this.stringify(arg[i]);
									if (json != null) out[out.length] = json;
								}
								return '[' + out.join(',') + ']';
								} else {
								for (var p in arg) {
									var json = this.stringify(arg[p]);
									if (json != null) out[out.length] = '"' + encodeString(p) + '":' + json;
								}
								return '{' + out.join(',') + '}';
								}
							}
						return 'null'; // if execution reaches here, arg is null.
						case 'boolean'  : return String(arg);
						// cases function & undefined return null implicitly.
					}
				},
				parse: function (text) {
					try {
						alert(text);
						return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(text.replace(/"(\\.|[^"\\])*"/g, ''))) && eval('(' + text + ')');
					} catch (e) {
						alert(e);
						return false;
					}
				}
			}
		}()
	}
}();

